Skip to content

test(cliffs): invert the elevation by level set - #18 is ONE TERM of vulcanus_elev - #82

Merged
wormeyman merged 1 commit into
mainfrom
cliffs-level-set-basalt-lakes-18
Aug 2, 2026
Merged

test(cliffs): invert the elevation by level set - #18 is ONE TERM of vulcanus_elev#82
wormeyman merged 1 commit into
mainfrom
cliffs-level-set-basalt-lakes-18

Conversation

@wormeyman

Copy link
Copy Markdown
Owner

The collapsed rule from #81 is also an instrument. A cell carries a cliff exactly when its corner elevations straddle cliff_elevation_0, so sweeping that threshold measures the elevation field the generator reads - the one thing an expression sample cannot do, since calculate_tile_properties answers for its own channel and whether the two agree was the open question.

19 levels, cliff_elevation_0 20..200 step 10, region [0,0], rule collapsed:

cliff_elevation_0 game ours ours/game
20 658 979 1.49
40 603 816 1.35
70 335 463 1.38
110 142 200 1.41
120 122 126 1.03
150 117 117 1.00
200 97 97 1.00

A clean edge at 120. At or above it every level is 1.00-1.04 and the same cells (both/game > 0.9, not merely the same count); below it every level is 1.20-1.49. Worst high 1.04 vs best low 1.20 - a threshold, not a drift, which is why the spec asserts the gap rather than two bounds separately.

Why 120 is not arbitrary

vulcanus_elev =
  vulcanus_elevation_offset
    + lerp(lerp(120 * vulcanus_basalt_lakes_multisample,
                20 + vulcanus_mountains_func * vulcanus_mountains_elevation_multiplier,
                vulcanus_mountains_biome),
           vulcanus_ashlands_func,
           vulcanus_ashlands_biome)

vulcanus_basalt_lakes is a min(1, ...), so the basalt-lakes branch saturates at exactly 120. Everything above 120 comes from the mountains and ashlands branches - which contain no multisample - and the port reproduces those exactly. Everything below is governed by vulcanus_basalt_lakes_multisample: the only multisample in the chain, and the only term with no Nauvis counterpart.

So the residual is that one term, and the port is exact everywhere else in the field. That is a far smaller target than "the Vulcanus elevation".

The mechanism to suspect

multisample's own docs: it evaluates "in a separate noise program with a larger grid" whose "sub-grids are copied to the main program". The cliff generator walks the 4-tile corner lattice; calculate_tile_properties does not - and that is the channel vulcanus-multisample-NOTES.md measured multisample(e,dx,dy) == e(x+dx,y+dy) through, and the channel every elevation fixture was captured through. A min() of four samples is an erosion operator, so a coarser effective grid smooths the field exactly the way a 40% over-placement implies.

What this does NOT say

Neither the multisample port nor the corner-fields fixture is refuted - both are correct in their channel. What has never been tested is whether the primitive behaves the same when the calling program's grid is 4 tiles rather than 1. The tile renderer, which consumes vulcanus_elevation through that same 1-tile channel, is not implicated by any of this.

Does not fix #18. pnpm run verify: 309 files clean, 1302 passed / 3 skipped, preview 12 passed.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GRx1CU29RKLsRuUAErEDRB

The collapsed rule is also an instrument. A cell carries a cliff exactly when
its corner elevations straddle cliff_elevation_0, so sweeping that threshold
measures the elevation field the GENERATOR reads - the one thing an expression
sample cannot do, since calculate_tile_properties answers for its own channel
and whether the two agree was the open question.

19 levels, cliff_elevation_0 20..200 step 10, region [0,0], rule collapsed:

  e0= 20  game 658  ours 979  1.49
  e0= 40  game 603  ours 816  1.35
  e0= 70  game 335  ours 463  1.38
  e0=110  game 142  ours 200  1.41
  e0=120  game 122  ours 126  1.03
  e0=150  game 117  ours 117  1.00
  e0=200  game  97  ours  97  1.00

A clean edge at 120. At or above it every level is 1.00-1.04 AND the same
cells (both/game > 0.9), not merely the same count; below it every level is
1.20-1.49. Worst high 1.04 vs best low 1.20, so this is a threshold, not a
drift - which is why the spec asserts the GAP rather than two bounds.

The edge is not arbitrary. vulcanus_elev is

  vulcanus_elevation_offset
    + lerp(lerp(120 * vulcanus_basalt_lakes_multisample,
                20 + vulcanus_mountains_func * ..._multiplier,
                vulcanus_mountains_biome),
           vulcanus_ashlands_func, vulcanus_ashlands_biome)

and vulcanus_basalt_lakes is a min(1, ...), so the basalt-lakes branch
SATURATES AT EXACTLY 120. Everything above 120 comes from the mountains and
ashlands branches - no multisample anywhere in them - and the port reproduces
those exactly. Everything below is governed by
vulcanus_basalt_lakes_multisample: the only multisample in the chain, and the
only term with no Nauvis counterpart.

So the residual is that one term, and the port is exact everywhere else in the
field. That is a much smaller target than "the Vulcanus elevation".

The mechanism to suspect is the one the primitive's own docs describe: it
evaluates "in a separate noise program with a larger grid" whose "sub-grids
are copied to the main program". The cliff generator walks the 4-tile corner
lattice; calculate_tile_properties does not - and that is the channel
vulcanus-multisample-NOTES measured multisample(e,dx,dy) == e(x+dx,y+dy)
through, and the channel every elevation fixture was captured through. A min()
of four samples is an erosion operator, so a coarser effective grid smooths
the field exactly the way a 40% over-placement implies.

What this does NOT say: neither the multisample port nor the corner-fields
fixture is refuted. Both are correct IN THEIR CHANNEL. What has never been
tested is whether the primitive behaves the same when the calling program's
grid is 4 tiles rather than 1 - and the tile renderer, which consumes
vulcanus_elevation through that same 1-tile channel, is not implicated.

DOES NOT FIX #18.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GRx1CU29RKLsRuUAErEDRB
@wormeyman
wormeyman merged commit b07b544 into main Aug 2, 2026
2 checks passed
wormeyman added a commit that referenced this pull request Aug 2, 2026
…#84)

#94 ruled out lava, every other tile, the cliffiness gate and entity collision,
then handed the mechanism over open: the cliff/ore correlation fits two stories
demanding opposite fixes, and the note closed "settle the direction, not add a
rejection."

Settled with a lever rather than an argument. `autoplace_controls` is settable
on the surface exactly like `cliff_settings`, so the resources can be switched
off (`size = 0`) and the same regions regenerated - #82's collapse trick pointed
one subsystem over.

| arm | cliff-vulcanus | resources |
| --- | --- | --- |
| `[1500,1500]` resources ON | 885 | 3,933 |
| `[1500,1500]` calcite OFF | 912 | 19 |
| `[1500,1500]` geyser OFF | 889 | 3,914 |
| `[1500,1500]` ALL OFF | 916 | 0 |
| `[0,0]` collapsed, ON | 335 | 945 |
| `[0,0]` collapsed, ALL OFF | **345** | 0 |

**Both arms, not one.** Ore off puts a cliff in all ten blob cells, 0/10 ->
10/10. The converse arm is what makes it a direction: the collapsed settings
force 335 cliffs through the region against the default's 283, straight through
the tungsten field, and the ore does not move - the same 945 entities.

Three properties, each constraining the mechanism: it is ONE-WAY (removing a
resource only ever adds cliffs, in all four paired arms), ADDITIVE (27 calcite +
4 geyser = exactly the 31 of all-off, disjoint), and LOCAL with a geometry that
is a BOX OVERLAP against the resource ENTITY's rectangle - measurable only
because the geyser's collision half-extent is 1.398 against the ores' 0.098,
which is why every point-at-tile-centre geometry scored 20-27 of 31. Scored as
box overlap: 21 of 31 explained, ZERO false alarms in 885, and all six connected
components of the suppressed set contain a directly-overlapped cell (adjacency
is not a free pass - only 8 of 885 kept cliffs touch one).

Worth 31 of the 42 cells the port over-places at `[1500,1500]`: every one of the
31 is a cell the port places and none is a cliff the game kept, so the rule is
pure precision.

Still not a collision, now doubly established. The mask argument stands and the
fixture carries the layers rather than a claim about them. The disassembly says
it more strongly: `computeInternal` (0x101622860) calls `generateCliffs` at +44
and `generateEntities` at +148; `apply` (0x101623b48) calls `applyCliffs` at
+124 and `applyEntities` at +164 - the cliffs are computed AND placed before any
resource exists, which also refutes #94's "maybe ore avoids cliffs" guess.

Harness: `sampleCliffEntitiesFull` gains `autoplaceControls`, `alsoResources`
and `protoNames`, dumping cliffs, resources, prototype collision geometry and
the controls the SURFACE read back from ONE generated surface - so an override
that failed to apply cannot be mistaken for a term that does not matter, and
"the ore moved" and "the cliffs moved" are never compared across two worlds.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LT6oy76sLRqoC1QzLocQ1h
wormeyman added a commit that referenced this pull request Aug 2, 2026
…#84) (#99)

#94 ruled out lava, every other tile, the cliffiness gate and entity collision,
then handed the mechanism over open: the cliff/ore correlation fits two stories
demanding opposite fixes, and the note closed "settle the direction, not add a
rejection."

Settled with a lever rather than an argument. `autoplace_controls` is settable
on the surface exactly like `cliff_settings`, so the resources can be switched
off (`size = 0`) and the same regions regenerated - #82's collapse trick pointed
one subsystem over.

| arm | cliff-vulcanus | resources |
| --- | --- | --- |
| `[1500,1500]` resources ON | 885 | 3,933 |
| `[1500,1500]` calcite OFF | 912 | 19 |
| `[1500,1500]` geyser OFF | 889 | 3,914 |
| `[1500,1500]` ALL OFF | 916 | 0 |
| `[0,0]` collapsed, ON | 335 | 945 |
| `[0,0]` collapsed, ALL OFF | **345** | 0 |

**Both arms, not one.** Ore off puts a cliff in all ten blob cells, 0/10 ->
10/10. The converse arm is what makes it a direction: the collapsed settings
force 335 cliffs through the region against the default's 283, straight through
the tungsten field, and the ore does not move - the same 945 entities.

Three properties, each constraining the mechanism: it is ONE-WAY (removing a
resource only ever adds cliffs, in all four paired arms), ADDITIVE (27 calcite +
4 geyser = exactly the 31 of all-off, disjoint), and LOCAL with a geometry that
is a BOX OVERLAP against the resource ENTITY's rectangle - measurable only
because the geyser's collision half-extent is 1.398 against the ores' 0.098,
which is why every point-at-tile-centre geometry scored 20-27 of 31. Scored as
box overlap: 21 of 31 explained, ZERO false alarms in 885, and all six connected
components of the suppressed set contain a directly-overlapped cell (adjacency
is not a free pass - only 8 of 885 kept cliffs touch one).

Worth 31 of the 42 cells the port over-places at `[1500,1500]`: every one of the
31 is a cell the port places and none is a cliff the game kept, so the rule is
pure precision.

Still not a collision, now doubly established. The mask argument stands and the
fixture carries the layers rather than a claim about them. The disassembly says
it more strongly: `computeInternal` (0x101622860) calls `generateCliffs` at +44
and `generateEntities` at +148; `apply` (0x101623b48) calls `applyCliffs` at
+124 and `applyEntities` at +164 - the cliffs are computed AND placed before any
resource exists, which also refutes #94's "maybe ore avoids cliffs" guess.

Harness: `sampleCliffEntitiesFull` gains `autoplaceControls`, `alsoResources`
and `protoNames`, dumping cliffs, resources, prototype collision geometry and
the controls the SURFACE read back from ONE generated surface - so an override
that failed to apply cannot be mistaken for a term that does not matter, and
"the ore moved" and "the cliffs moved" are never compared across two worlds.


Claude-Session: https://claude.ai/code/session_01LT6oy76sLRqoC1QzLocQ1h

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Vulcanus cliffs: 12.5% of matched cells get the wrong cliff_orientation (residual after the placement-count fixes)

1 participant